﻿using CWCS.BLL.WebsocketCommon;
using CWCS.Common.Log;
using CWCS.Entity.SRM;
using Nti56.Modbus.Communication.Models;
using Nti56.Modbus.Communication.Services;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;

namespace CWCS.BLL
{

    public class ModbusBLLSRM : BLLBase
    {
        private LogHelper log;
        private LogHelper logConnect;

        public delegate void Notify(string type, string msg);
        public event Notify ENotify;

        public delegate void ShowEquipmentStatus(CRCObject co, List<CRCStatus> cs);
        public event ShowEquipmentStatus ShowStatus;

        public Hashtable loght = Hashtable.Synchronized(new Hashtable());
        private delegate void ReadOPCInfos(object info, object belong);
        private event ReadOPCInfos ReadEquipOPCInfo;

        private delegate void ReConnetPLC(string ip, int retryCount);
        private event ReConnetPLC SRMReConnectPLC;
        private static readonly object objLoc = new object();

        ModbusReadWriteService readService = new ModbusReadWriteService();
        ModbusReadWriteService writeService = new ModbusReadWriteService();
        public Dictionary<string, bool> PLCConnectStatus = new Dictionary<string, bool>();
        public static Dictionary<string, ModbusTcpClient> modbusReadClients = new Dictionary<string, ModbusTcpClient>();
        public static Dictionary<string, ModbusTcpClient> modbusWriteClients = new Dictionary<string, ModbusTcpClient>();
        protected static Dictionary<string, ModbusConnectionInfo> ModbusList = new Dictionary<string, ModbusConnectionInfo>();//PLC列表

        public void InitData(string strsrmno, string ipaddress, int port, ForkNoType forktype)//构造函数
        {
            string error = "";
            log = new LogHelper("SC");
            logConnect = new LogHelper("SCConnect");
            CRCStatus srm_info = new CRCStatus();
            srm_info.scno = strsrmno;
            srm_info.RackSide = "1";
            srm_info.RackPosition = "1";
            srm_info.RackPlace = "1";
            srm_info.RackLevel = "1";
            srm_info.RackDepth = "1";
            srm_info.AlarmMessage = "未连接";
            srm_info.ForkType = forktype == ForkNoType.forkNo ? "forkNo" : "forkNo1";
            string wcsModeInfo = "任务:" + getTasktype(srm_info.TaskType);
            string wcsForkInfo = ".货叉:" + getForktype(srm_info.ForkType);
            srm_info.LabelInfo = ".任务号:0.托盘1:.托盘2:.当前位置:无.模式:无" + ".状态:无" + ".起始地址:" + ".目标地址:" + "." + srm_info.AlarmMessage;
            srm_info.NewLabelInfo = wcsModeInfo + wcsForkInfo + srm_info.LabelInfo;
            list_srm_info.Add(srm_info);

            if (InitData(ipaddress, port, ref error))
            {
                WriteLog("C", "连接成功,IP地址:" + ipaddress + ",堆垛机编号:" + strsrmno + ",端口:" + port);
            }
            else
            {
                WriteLog("C", "连接失败,IP地址:" + ipaddress + ",堆垛机编号:" + strsrmno + ",端口:" + port);
            }
        }
        public ModbusBLLSRM()
        {
            this.ReadEquipOPCInfo += new ReadOPCInfos(ReadSRMInfo);
            this.SRMReConnectPLC += new ReConnetPLC(ReConnect);
        }
        public static string RemoveNotNumber(string key)
        {
            return Regex.Replace(key, @"[^\d]*", "");
        }

        public bool SendCode(string scno, int type, int taskno, string destAdress, string fireflag, int forkNo = 1, int fork2taskno = 0)
        {
            try
            {
                destAdress = RemoveNotNumber(destAdress); //S0010010011
                byte[] msg = new byte[22];
                int fork2taskno1 = 0;
                int fork2taskno2 = 0;
                if (fork2taskno != 0)
                {
                    fork2taskno1 = fork2taskno / 256;
                    fork2taskno2 = fork2taskno % 256;
                }
                int taskno1 = taskno / 256;
                int taskno2 = taskno % 256;
                int actionpoint = 1;
                int aisle1 = 1;
                msg[0] = (byte)fork2taskno1;
                msg[1] = (byte)fork2taskno2;
                msg[2] = (byte)taskno1;
                msg[3] = (byte)taskno2;
                msg[4] = (byte)type;//取: 112   放: 128   清除: 0
                msg[5] = 0x00;
                msg[6] = 0x00;
                msg[7] = (byte)forkNo;// 0x01货叉1  0x02货叉2  0x03货叉同取同放
                aisle1 = int.Parse(scno.Substring(scno.Length - 1));//堆垛机号 1-3
                //aisle1 = 1;
                // 取货完成 清除  &&  放货完成 清除
                if (string.IsNullOrEmpty(destAdress) && type == 0)
                {
                    for (int i = 10; i < 22; i++)
                    {
                        msg[i] = 0x00;
                    }
                }
                // 取放指令为：站台 需要配置
                else if (destAdress != null && destAdress.Length == 4)
                {
                    CRCObject si = list_srm_object.Find(
                          delegate (CRCObject cc)
                       {
                           return cc.ScNo == scno;
                       }
                     );
                    if (si != null)
                    {
                        actionpoint = int.Parse(si.scStation[destAdress]);
                    }
                    for (int i = 10; i < 22; i++)
                    {
                        msg[i] = 0x00;
                    }

                }
                //取放指令为：货架
                else if (destAdress != null && destAdress.Length > 8)  //0504606
                {

                    //int row = int.Parse(destAdress.Substring(0, 3)) % 2 == 0 ? 2 : 1;

                    int row = 0;
                    if (scno == "SC03" || scno == "SC04"|| scno == "SC08"|| scno == "SC09")
                        row = int.Parse(destAdress.Substring(0, 3)) % 2 == 0 ? 1 : 2;
                    else 
                        row = int.Parse(destAdress.Substring(0, 3)) % 2 == 0 ? 2 : 1;

                    int Column = int.Parse(destAdress.Substring(3, 3));
                    int layer = int.Parse(destAdress.Substring(6, 3));
                    int depth = 1;
                    msg[10] = (byte)row;
                    msg[11] = (byte)Column;  //直接给列数，无货格概念
                    msg[12] = (byte)1;
                    msg[13] = (byte)(layer);//层,1-9
                    msg[14] = (byte)(depth);//深 1-2
                    for (int i = 15; i < 22; i++)
                    {
                        msg[i] = 0x00;
                    }
                }
                msg[8] = (byte)actionpoint;
                msg[9] = (byte)aisle1;//巷道
                msg[19] = (byte)(int.Parse(fireflag));

                bool sendflag = Write(scno, msg);
                if (sendflag)
                {
                    WriteLog("S", "堆垛机编号:" + scno + "发送指令成功:" + type.ToString() + "工位1任务号:" + taskno.ToString() + "工位2任务号:" + fork2taskno.ToString() + "地址:" + destAdress + ",货叉:" + forkNo);
                }
                else
                {
                    WriteLog("S", "堆垛机编号:" + scno + "发送指令失败:" + type.ToString() + "工位1任务号:" + taskno.ToString() + "工位2任务号:" + fork2taskno.ToString() + "地址:" + destAdress + ",货叉:" + forkNo);
                }
                return sendflag;
            }
            catch (Exception ee)
            {
                WriteLog("S", "堆垛机编号:" + scno + "发送指令异常:" + type.ToString() + "/" + taskno.ToString() + "/" + destAdress + "  " + ee.Message);
                return false;
            }
        }

        public void WriteLog(string type, string msg)
        {
            if (!string.IsNullOrEmpty(msg))
            {
                ShowText(type, msg);
                if (type == "S")
                {
                    log.Info(msg);
                }
                else
                {
                    logConnect.Info(msg);
                }

                ScMsg scMsg = new ScMsg();
                scMsg.type = type;
                scMsg.Msg = msg;
                var scMsgString = Newtonsoft.Json.JsonConvert.SerializeObject(scMsg);
                WebSocketHelper.SendMsgToRemotePoint("ScMsg" + scMsgString);
            }
        }

        /// <summary>
        /// 下发任务
        /// </summary>
        /// <param name="num">堆垛机编号</param>
        /// <param name="taskno">工位1任务号</param>
        /// <param name="fromstation">工位1起始地址</param>
        /// <param name="tostation">工位1目标地址</param>
        /// <param name="statostation">工位1输送机目标地址</param>
        /// <param name="tasktype">任务类型</param>
        /// <param name="forkNo">货叉下发命令值</param>
        /// <param name="taskno1">工位2任务号</param>
        /// <param name="fromstation1">工位2起始地址</param>
        /// <param name="tostation1">工位2目标地址</param>
        /// <param name="statostation1">工位2输送机目标地址</param>
        /// <returns></returns>
        public bool SendTask(string num, string taskno, string fromstation, string tostation, string statostation, string tasktype, int forkNo = 1, int taskno1 = 0, string fromstation1 = "", string tostation1 = "", string statostation1 = "")
        {
            try
            {
                if (modbuscrc.UpdateCRCTaskInfo(num, fromstation.Trim('S'), tostation.Trim('S'), statostation, tasktype, forkNo, fromstation1, tostation1, statostation1))
                {
                    bool sendflag = false;
                    if (forkNo == 2)//货叉2取货，则下发货叉2的任务起始地址
                    {
                        if (taskno == taskno1.ToString())
                            taskno = "0";
                        sendflag = SendCode(num, CR_GETC, int.Parse(taskno), fromstation1, "0", forkNo, taskno1); //取货指令
                    }
                    else
                    {
                        if (taskno == taskno1.ToString())
                            taskno1 = 0;
                        sendflag = SendCode(num, CR_GETC, int.Parse(taskno), fromstation, "0", forkNo, taskno1); //取货指令
                    }
                    if (sendflag)
                    {
                        if (forkNo == 1 || forkNo == 3)
                        {
                            //if (fromstation.Length == 4)
                            //{
                            //    ConveryBLL converyBLL = new ConveryBLL();
                            //    STACommand cmd = STACommand.ClearToStation;
                            //    converyBLL.WriteStationInfo(cmd, new string[2] { fromstation, "2" }, fromstation, 4, 2);
                            //}
                            dal.UpdateTaskStatus(fromstation, taskno.ToString(), "", "SC", "2", "1"); //修改堆垛机货叉1任务状态
                            WriteLog("S", "堆垛机编号:" + num + "发送任务成功,货叉1任务号:" + taskno.ToString() + "  起止:" + fromstation + "-->" + tostation);
                        }
                        if (forkNo == 2 || forkNo == 3)
                        {
                            //if (fromstation1.Length == 4)
                            //{
                            //    ConveryBLL converyBLL = new ConveryBLL();
                            //    STACommand cmd = STACommand.ClearToStation;
                            //    converyBLL.WriteStationInfo(cmd, new string[2] { fromstation1, "2" }, fromstation1, 4, 2);
                            //}
                            dal.UpdateTaskStatus(fromstation1, taskno1.ToString(), "", "SC", "2", "1"); //修改堆垛机货叉2任务状态
                            WriteLog("S", "堆垛机编号:" + num + "发送任务成功,货叉2任务号:" + taskno1.ToString() + "  起止:" + fromstation1 + "-->" + tostation1);
                        }
                    }
                    else
                    {
                        WriteLog("S", "堆垛机编号:" + num + "发送任务发送失败,任务号:" + taskno.ToString() + "  起止:" + fromstation + "-->" + tostation);
                    }
                    return sendflag;
                }
                else
                {
                    WriteLog("S", "堆垛机编号:" + num + "发送任务更新失败,任务号:" + taskno.ToString() + "  起止:" + fromstation + "-->" + tostation);
                    return false;
                }
            }
            catch (Exception ee)
            {
                WriteLog("S", "堆垛机编号:" + num + "发送任务异常:" + taskno + "(" + fromstation + "-->" + tostation + ") " + ee.ToString());
                return false;
            }
        }

        public string getTasktype(string TaskType)
        {
            if (TaskType == "All")
            {
                return "正常出入";
            }
            else if (TaskType == "None")
            {
                return "暂停任务";
            }
            else if (TaskType == "Task_InBound")
            {
                return "仅入库";
            }
            else if (TaskType == "Task_OutBound")
            {
                return "仅出库";
            }
            return null;
        }

        public string getForktype(string TaskType)
        {
            if (TaskType == "forkNo")
            {
                return "双货叉";
            }
            else if (TaskType == "forkNo1")
            {
                return "货叉1";
            }
            else if (TaskType == "forkNo2")
            {
                return "货叉2";
            }
            return null;
        }
        public void ShowText(string type, string msg)
        {
            if (ENotify != null)
            {
                ENotify(type, msg);
            }
        }
        protected void ShowUI(CRCObject co)
        {
            if (co != null)
            {
                CRCObject cco = list_srm_object.Find(
                                delegate (CRCObject c)
                                {
                                    return c.ScNo == co.ScNo;
                                }
                            );
                if (cco != null)
                {
                    ShowStatus(cco, list_srm_info);
                    ScInfo scInfo = new ScInfo();
                    scInfo.sCTaskMode = BLLBase.modeFlag[co.ScNo];
                    scInfo.cco = cco;
                    scInfo.list_srm_info = list_srm_info;
                    var scInfoString = Newtonsoft.Json.JsonConvert.SerializeObject(scInfo);
                    WebSocketHelper.SendMsgToRemotePoint("ScInfo" + scInfoString);
                }
            }
        }
        public bool InitData(string ip, int port, ref string error)
        {
            try
            {
                if (!ModbusList.ContainsKey(ip))
                {
                    ModbusConnectionInfo connectionInfo = new ModbusConnectionInfo()
                    {
                        Ip = IPAddress.Parse(ip),
                        Port = port,
                        Station = 1
                    };
                    ModbusList.Add(ip, connectionInfo);
                    PLCConnectStatus.Add(ip, false);
                    var Readconnect = ModbusConnection.Connection(connectionInfo, "R" + ip);
                    var Writeconnect = ModbusConnection.Connection(connectionInfo, "W" + ip);
                    modbusReadClients[ip] = Readconnect.Value;
                    modbusWriteClients[ip] = Writeconnect.Value;
                    if (Readconnect.Value.Client.Connected && Writeconnect.Value.Client.Connected)
                    {
                        if (!PLCConnectStatus.ContainsKey(ip))
                        {
                            PLCConnectStatus.Add(ip, true);
                        }
                        return true;
                    }
                    else
                    {
                        if (!PLCConnectStatus.ContainsKey(ip))
                        {
                            PLCConnectStatus.Add(ip, false);
                        }
                        return false;
                    }
                }
            }
            catch (Exception ex)
            {
                error = ex.Message;
            }
            return false;
        }

        public void Run(object i_Index)
        {
            Thread threadCom = new Thread(new ParameterizedThreadStart(TimeScan));
            threadCom.IsBackground = true;
            threadCom.Start(i_Index);
        }

        private void TimeScan(object belong)
        {
            while (true)
            {
                if (list_srm_object != null)
                {
                    foreach (var sc in list_srm_object.Where(x => x.belong == belong.ToString()))
                        ReadSRMInfo(sc, belong);
                }
            }
        }

        /// <summary>
        /// 读取所属线程所有站台信息
        /// </summary>
        /// <param name="stationinfo"></param>
        private void ReadSRMInfo(object obj, object belong)
        {
            if (obj != null)
            {
                CRCObject sc = (CRCObject)obj;
                Byte[] scBuffer = new byte[sc.readlength * 2];
                if (modbusReadClients[sc.IpAddress].Client.Connected)
                {
                    var result = readService.ReadByte(sc.readaddress, (ushort)(sc.readlength * 2), modbusReadClients[sc.IpAddress], "R" + sc.ScNo);
                    if (result.IsSucceed)
                    {
                        result.Value.CopyTo(scBuffer, 0);
                        byte[] byttemp = new byte[scBuffer.Length - 4];
                        Array.Copy(scBuffer, 4, byttemp, 0, byttemp.Length);
                        byttemp = byttemp.Select((x, i) => new { x, i }).GroupBy(x => x.i / 2).SelectMany(x => new byte[] { x.Last().x, x.First().x }).ToArray();
                        Array.Copy(byttemp, 0, scBuffer, 4, byttemp.Length);
                        sc.connectstatus = 1;
                        modbuscrc.UpdateCRCStatus(sc.ScNo, "Info", scBuffer);
                    }
                    else
                    {
                        ReConnect(sc.IpAddress, 1);
                        sc.connectstatus = 0;
                        modbuscrc.UpdateCRCStatus(sc.ScNo, "Break", scBuffer);
                    }
                }
                else
                {
                    ReConnect(sc.IpAddress, 1);
                    sc.connectstatus = 0;
                    modbuscrc.UpdateCRCStatus(sc.ScNo, "Break", scBuffer);
                }
                ShowUI(sc);
                Thread.Sleep(150);
            }
        }
        /// <summary>
        /// 重新连接
        /// </summary>
        /// <param name="strIP"></param>
        /// <param name="retryCount"></param>
        private void ReConnect(string strIP, int retryCount = 1)
        {
            try
            {
                if (SRMReConnectPLC != null)
                {
                    lock (objLoc)
                    {
                        WriteLog("C", string.Format("PLC:{0},连接断开,系统开始自动重连...", strIP));
                        if (ModbusList.ContainsKey(strIP))
                        {
                            bool flag = true;
                            int i_Count = 1;
                            while (flag)
                            {
                                if (i_Count > retryCount)
                                    break;
                                //断开连接
                                modbusReadClients[strIP].Client?.Close();
                                modbusWriteClients[strIP].Client?.Close();
                                WriteLog("C", string.Format("PLC：{0}，尝试连接次数：{1}", strIP, i_Count));
                                var Readconnect = ModbusConnection.Connection(ModbusList[strIP], "R" + strIP);
                                var Writeconnect = ModbusConnection.Connection(ModbusList[strIP], "W" + strIP);
                                modbusReadClients[strIP] = Readconnect.Value;
                                modbusWriteClients[strIP] = Writeconnect.Value;
                                if (Readconnect.Value.Client.Connected && Writeconnect.Value.Client.Connected)
                                {
                                    WriteLog("C", string.Format("PLC：{0}，连接成功", strIP, i_Count));
                                    flag = false;
                                    break;
                                }
                                else
                                {
                                    i_Count++;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                WriteLog("Break", string.Format("PLC：{0},重新链接失败！", strIP));
            }
        }


        /// <summary>
        /// 写入值
        /// </summary>
        /// <param name="scno"></param>
        /// <param name="str">写入数组值</param>
        /// <param name="index">地址偏移值</param>
        /// /// <param name="index">长度</param>
        /// <returns></returns>
        public bool Write(string scno, byte[] str)
        {
            try
            {
                CRCObject si = list_srm_object.Find(
                   delegate (CRCObject sc)
                   {
                       return sc.ScNo == scno;
                   }
               );
                if (si != null)
                {
                    if (si.connectstatus == 1)
                    {
                        byte[] byttemp = new byte[str.Length - 4];
                        Array.Copy(str, 4, byttemp, 0, byttemp.Length);
                        byttemp = byttemp.Select((x, i) => new { x, i }).GroupBy(x => x.i / 2).SelectMany(x => new byte[] { x.Last().x, x.First().x }).ToArray();
                        Array.Copy(byttemp, 0, str, 4, byttemp.Length);

                        if (modbusWriteClients[si.IpAddress].Client.Connected)
                        {
                            if (writeService.WriteByte(si.writeaddress, str, modbusWriteClients[si.IpAddress], "W" + scno).IsSucceed)
                            {
                                return true;
                            }
                        }
                        return false;
                    }
                    else
                        return false;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return false;
        }
    }
}
